package net.jmchilton.spring.collect;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;


/**
 * <p>This is the base class for all of the Spring collection {@link 
 * org.springframework.beans.factory.xml.NamespaceHandler} classes 
 * (one for each namespace). These classes are responsible for 
 * registering BeanDefinitionParser's for handling the extension 
 * XML elements for each namespace.</p>
 * 
 * <p>See Spring's documentation for a description for more information on 
 * the {@link org.springframework.beans.factory.xml.NamespaceHandler} 
 * interface.</p>
 * 
 * <p>Subclasses of this object should register themselves with Spring and
 * should have a file called {@code static-factories.properties} in their
 * package on the classpath. Lines of this file should be of the form:</p>
 * 
 * <p><i>element</i>=<i>factory_class</i>,<i>factory_method</i></p>
 * 
 * <p>For instance,</p> 
 * 
 * <p><code>collection-to-enumation=java.util.Collections,enumeration</code></p>
 *  
 * <p>Elements with name <i>element</i> will be translated to Spring
 * beans with its <code>class</code> attribute defined as <i>factory_class</i> and 
 * the <code>factory-method</code> attribute set as <i>factory_method</i>.</p>
 * 
 * @see org.springframework.beans.factory.xml.NamespaceHandlerSupport
 * @see org.springframework.beans.factory.xml.NamespaceHandler
 * 
 * @author John Chilton
 *
 */
public abstract class BaseNamespaceHandler extends NamespaceHandlerSupport {
  static interface TemplateParserFactory {
    BeanDefinitionParser get(String clazz, String method);
  }
    
  private Properties loadProperties(final String resourceName) {
    final Properties props = new Properties();
    // Fetch resources from concrete subclass's package.  
    final InputStream inputStream = this.getClass().getResourceAsStream(resourceName); 
    try {
      props.load(inputStream);
    } catch (final IOException e) {
      throw new RuntimeException(e);
    } finally {
      try {
        inputStream.close();
      } catch(final IOException e) {
        e.printStackTrace();
      }
    }
    return props;
  }
  
  private Map<String, List<String>> getPropertiesMap(final String resourceName) {
    final Map<String, List<String>> propertiesMap = new HashMap<String, List<String>>();
    final Properties props = this.loadProperties(resourceName);
    for(final Map.Entry<Object, Object> entry : props.entrySet()) {
      final String value = (String) props.get(entry.getKey());
      final String[] values = value.split("\\s*,\\s*");
      propertiesMap.put((String) entry.getKey(), Arrays.asList(values));
    }
    return propertiesMap;
  }
  
  static class FactoryTemplateParserFactory implements TemplateParserFactory {
    public BeanDefinitionParser get(final String clazz, final String method) {
      return new FactoryMethodTemplateBeanParser(clazz, method);
    }    
  }

  /*
  class ListTemplateParserFactory implements TemplateParserFactory {
    public BeanDefinitionParser get(String clazz, String method) {
      return new ListTemplateParser(clazz,method);
    }
  }

  class SetTemplateParserFactory implements TemplateParserFactory {
    public BeanDefinitionParser get(String clazz, String method) {
      return new SetTemplateParser(clazz,method);
    }
  }

  class MapTemplateParserFactory implements TemplateParserFactory {
    public BeanDefinitionParser get(String clazz, String method) {
      return new MapTemplateParser(clazz,method);
    }
  }
  */
  
  /**
   * Sets up bean definition parsers for each element using the 
   * {@code static-factories.properties} defined in the concrete
   * subclass's package.
   * 
   */
  public final void init() {
    final Map<String, TemplateParserFactory> map = new HashMap<String, TemplateParserFactory>();
    map.put("static-factories.properties", new FactoryTemplateParserFactory());
    //map.put("list.properties", new ListTemplateParserFactory());
    //map.put("set.properties", new SetTemplateParserFactory());
    //map.put("map.properties", new MapTemplateParserFactory());
    for(final Map.Entry<String, TemplateParserFactory> parserEntry : map.entrySet()) {
      for(final Map.Entry<String, List<String>> entry : this.getPropertiesMap(parserEntry.getKey()).entrySet()) {
        final List<String> values = entry.getValue();
        final String clazz = values.get(0);
        final String method= values.get(1);
        /*
          String method = values.size() < 2 ? null : values.get(1);
          method = (method == null || method.equals("")) ? null : method;
        */
        super.registerBeanDefinitionParser(entry.getKey(), parserEntry.getValue().get(clazz, method));      
      }
    }
  }
}
